﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace _9._1
{
    class Program
    {
        // 用于存储已经访问过的 URL
        static ConcurrentDictionary<string, bool> visitedUrls = new ConcurrentDictionary<string, bool>();
        // 用于存储找到的电话号码
        static ConcurrentBag<string> phoneNumbers = new ConcurrentBag<string>();
        // 锁对象
        static object countLock = new object();
        // 计数器，用于记录找到的电话号码数量
        static int count = 0;

        static void Main(string[] args)
        {
            Console.WriteLine("请输入要搜索的关键字：");
            string keyword = Console.ReadLine();
            Crawl(keyword);
            Console.WriteLine("已完成爬取，找到的电话号码如下：");
            foreach (var phoneNumber in phoneNumbers)
            {
                Console.WriteLine(phoneNumber);
            }
            Console.WriteLine("按任意键退出程序。");
            Console.ReadKey();
        }

        // 爬取指定关键字相关的网站
        static void Crawl(string keyword)
        {
            // 搜索引擎列表
            string[] searchEngines = { "https://www.baidu.com/s?wd=", "https://www.bing.com/search?q=" };
            Parallel.ForEach(searchEngines, searchEngine =>
            {
                string url = searchEngine + keyword;
                ExtractPhoneNumber(url).Wait();
            });
        }

        // 从指定 URL 提取电话号码
        static async Task ExtractPhoneNumber(string url)
        {
            if (visitedUrls.TryAdd(url, true) && count < 100)
            {
                Console.WriteLine("正在访问：" + url);
                string html = await DownloadHtml(url);
                if (html != null)
                {
                    var phoneNumbersInHtml = FindPhoneNumbers(html);
                    foreach (string phoneNumber in phoneNumbersInHtml)
                    {
                        if (!phoneNumbers.Contains(phoneNumber))
                        {
                            phoneNumbers.Add(phoneNumber);
                            Console.WriteLine("找到电话号码：" + phoneNumber + "，所属的 URL：" + url);
                            lock (countLock)
                            {
                                count++;
                                if (count == 100) return; // 找到100个电话号码，直接返回
                            }
                        }
                    }
                    var linksInHtml = FindLinks(html);
                    List<Task> tasks = new List<Task>();
                    foreach (string link in linksInHtml)
                    {
                        tasks.Add(ExtractPhoneNumber(link));
                    }
                    await Task.WhenAll(tasks);
                }
            }
        }

        // 下载指定 URL 的 HTML 内容
        static async Task<string> DownloadHtml(string url)
        {
            try
            {
                using WebClient client = new WebClient { Encoding = System.Text.Encoding.UTF8 };
                string html = await client.DownloadStringTaskAsync(url);
                return html;
            }
            catch (WebException ex)
            {
                Console.WriteLine("访问 URL " + url + " 失败，错误信息：" + ex.Message);
                return null;
            }
        }

        // 查找 HTML 中的电话号码
        static string[] FindPhoneNumbers(string html)
        {
            string[] patterns = { @"\d{3}-\d{8}", @"\d{4}-\d{7}", @"\d{7,8}", @"\d{3}-\d{4}-\d{4}" };
            List<string> phoneNumbers = new List<string>();
            foreach (string pattern in patterns)
            {
                MatchCollection matches = Regex.Matches(html, pattern);
                phoneNumbers.AddRange(matches.Cast<Match>().Select(match => match.Value));
            }
            return phoneNumbers.ToArray();
        }

        // 查找 HTML 中的链接
        static string[] FindLinks(string html)
        {
            MatchCollection matches = Regex.Matches(html, @"<a\s+.*?href=['""]?([^'"">\s]+)['""]?");
            return matches.Cast<Match>().Select(match => match.Groups[1].Value).Where(link => link.StartsWith("http")).ToArray();
        }
    }
}

